package com.zzjx.oauth2.common.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.redxun.common.context.TenantContextHolder;
import com.redxun.common.tool.BeanUtil;
import com.redxun.common.tool.constant.CommonConstant;
import com.zzjx.oauth2.common.properties.SecurityProperties;
import com.zzjx.oauth2.common.util.AuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.util.AntPathMatcher;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author zhaojiaxu
 * @date 2021/08/03 16:47
 * 请求权限判断service
 */
@Slf4j
public abstract class DefaultPermissionServiceImpl {

    @Autowired
    private SecurityProperties securityProperties;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    public abstract Map<String,Set<String>> selectApisByGroupIdsAndRedis();

    public boolean hasPermission(Authentication authentication, String requestMethod, String requestURI) {
        requestURI = requestURI.substring(requestURI.indexOf("/",requestURI.indexOf("/")+1 ));
        // 前端跨域OPTIONS请求预检放行 也可通过前端配置代理实现
        if (HttpMethod.OPTIONS.name().equalsIgnoreCase(requestMethod)) {
            return true;
        }
        if (!(authentication instanceof AnonymousAuthenticationToken)) {
            //判断是否开启url权限验证
            if (!securityProperties.getAuth().getUrlPermission().getEnable()) {
                return true;
            }
            //超级管理员admin不需认证
            String username = AuthUtils.getUsername(authentication);
            if (CommonConstant.ADMIN_USER_NAME.equals(username)) {
                return true;
            }

            OAuth2Authentication auth2Authentication = (OAuth2Authentication)authentication;
            //判断应用黑白名单
            if (!isNeedAuth(auth2Authentication.getOAuth2Request().getClientId())) {
                return true;
            }

            //判断不进行url权限认证的api，所有已登录用户都能访问的url
            for (String path : securityProperties.getAuth().getUrlPermission().getIgnoreUrls()) {
                if (antPathMatcher.match(path, requestURI)) {
                    return true;
                }
            }

            List<SimpleGrantedAuthority> grantedAuthorityList = (List<SimpleGrantedAuthority>) authentication.getAuthorities();
            if (CollectionUtil.isEmpty(grantedAuthorityList)) {
                log.warn("角色列表为空：{}", authentication.getPrincipal());
                return false;
            }

            //保存租户信息
            String clientId = auth2Authentication.getOAuth2Request().getClientId();
            TenantContextHolder.setTenant(clientId);

            //redis获取
            Map<String, Set<String>> apiDtoMap = selectApisByGroupIdsAndRedis();

            Set<String> groupIdSet = getGrantUrlGroupId(apiDtoMap,requestURI,requestMethod);

            boolean isIncludeGroupId = false;
            for (GrantedAuthority au : grantedAuthorityList) {
                if (groupIdSet!=null&&groupIdSet.contains(au.getAuthority())) {
                    isIncludeGroupId = true;
                    break;
                }
            }
            if (!isIncludeGroupId) {
                log.warn("Access is denied! Url:" + requestURI + " User:"
                        + username);
                return false;
            }
            return true;
        }
        return false;
    }

    private Set<String> getGrantUrlGroupId(Map<String,Set<String>> apiDtoMap,String requestURI,String requestMethod) {
        // /user/org/getUser
        Set<String> groupIdSet = apiDtoMap.get(requestURI);
        if (BeanUtil.isNotEmpty(groupIdSet)) {
            return groupIdSet;
        }
        // /user/org/getUser:GET
        groupIdSet = apiDtoMap.get(requestURI + ":" + requestMethod);
        if (BeanUtil.isNotEmpty(groupIdSet)) {
            return groupIdSet;
        }
        for (String url : apiDtoMap.keySet()) {
            String tempUrl = url;
            String method = null;
            if (url.contains(":")) {
                tempUrl = url.substring(0, url.indexOf(":"));
                method = url.substring(url.indexOf(":") + 1);
            }
            // /user/org/**:GET
            if (antPathMatcher.match(tempUrl, requestURI)) {
                if (StringUtils.isEmpty(method) || requestMethod.equals(method)) {
                    groupIdSet = apiDtoMap.get(url);
                    break;
                }
            }
        }
        return groupIdSet;
    }

    /**
     * 判断应用是否满足白名单和黑名单的过滤逻辑
     * @param clientId 应用id
     * @return true(需要认证)，false(不需要认证)
     */
    private boolean isNeedAuth(String clientId) {
        boolean result = true;
        //白名单
        List<String> includeClientIds = securityProperties.getAuth().getUrlPermission().getIncludeClientIds();
        //黑名单
        List<String> exclusiveClientIds = securityProperties.getAuth().getUrlPermission().getExclusiveClientIds();
        if (includeClientIds.size() > 0) {
            result = includeClientIds.contains(clientId);
        } else if(exclusiveClientIds.size() > 0) {
            result = !exclusiveClientIds.contains(clientId);
        }
        return result;
    }
}
